#ifndef TB_LAZY_HPP__
#define TB_LAZY_HPP__

#include <tbb/compat/thread>

TB_NAMESPACE_BEGIN

template <typename ValueType>
struct DefaultLazyAllocatorTrait {
  static ValueType* New() { return new ValueType(); }
  static void Delete(ValueType* x) { delete x; x = 0; }
};

template <
  typename ValueType,
  typename AllocatorTraits = DefaultLazyAllocatorTrait<ValueType> >
class Lazy {
public:
  ~Lazy() {
    ForceDestroy();
  }
  ValueType *Value() {
    beingCreatedMarker_ = reinterpret_cast<ValueType *>(1);

    ValueType *val = ins_.load(memory_order_acquire);
    if (val != 0 && val != beingCreatedMarker_) {
      return val;
    }

    ValueType* expect = 0;
    if (ins_.compare_exchange_strong(
      expect, beingCreatedMarker_, memory_order_acquire)) {
      ValueType* ins = AllocatorTraits::New();
      ins_.store(ins, memory_order_release);
      return ins;
    }

    while (true) {
      val = ins_.load(memory_order_relaxed);
      if (val != beingCreatedMarker_) {
        break;
      }
      std::this_thread::yield();
    }
    return val;
  }

  void ForceDestroy() {
    ValueType* val = ins_.exchange(0, memory_order_acquire);
    AllocatorTraits::Delete(val);
  }
private:
  ValueType* beingCreatedMarker_;
  atomic<ValueType *> ins_;
};

TB_NAMESPACE_END

#endif // TB_LAZY_HPP__
